package com.example.project.projectspringcloud.service.impl.order;


import com.example.project.projectspringcloud.common.Constants;
import com.example.project.projectspringcloud.common.wechat.MessageUtil;
import com.example.project.projectspringcloud.common.wechat.PackageUtil;
import com.example.project.projectspringcloud.common.wechat.XMLParse;
import com.example.project.projectspringcloud.dao.sys.DictionaryMapper;
import com.example.project.projectspringcloud.dao.sys.SysConfigMapper;
import com.example.project.projectspringcloud.entity.order.Order;
import com.example.project.projectspringcloud.entity.sys.Dictionary;
import com.example.project.projectspringcloud.entity.sys.SysConfig;
import com.example.project.projectspringcloud.entity.user.UserProfile;
import com.example.project.projectspringcloud.service.order.WeChatPayService;
import com.example.project.projectspringcloud.utils.DateUtil;
import com.example.project.projectspringcloud.utils.HttpUtil;
import com.example.project.projectspringcloud.utils.WebUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 微信支付 service
 *
 * @author Administrator
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class WeChatPayServiceImpl implements WeChatPayService {

    private static final Logger logger = LoggerFactory.getLogger(WeChatPayServiceImpl.class);

    @Autowired
    private SysConfigMapper sysConfigMapper;

    @Autowired
    private DictionaryMapper dictionaryMapper;

    /**
     * 微信支付请求
     * ==============================================================================
     *
     * @return
     */
    @Override
    public String getWxpayUrl(String requestId, String type) throws Exception {
        // 获得微信支付配置
        Map<String, String> websitemap = getWechatPayInfo();
        // timestamp 时间点
        String timeStanmp = PackageUtil.getTimeStamp();
        // noncestr 随机字符串
        String nonceStr = PackageUtil.getNonceStr();
        SortedMap<String, String> signParams = new TreeMap<String, String>();
        // qppid
        signParams.put("appid", websitemap.get("appid"));
        // 商户号id
        signParams.put("mch_id", websitemap.get("mchid"));
        // 时间戳
        signParams.put("time_stamp", timeStanmp);
        // 随机字符串
        signParams.put("nonce_str", nonceStr);
        // 对外支付订单号
        signParams.put("product_id", requestId + "#" + type);

        String sign = PackageUtil.createSign(signParams, websitemap.get("payKey"));

        String result = String.format("weixin://wxpay/bizpayurl" +
                        "?sign=%s" +
                        "&appid=%s" +
                        "&mch_id=%s" +
                        "&product_id=%s#%s" +
                        "&time_stamp=%s" +
                        "&nonce_str=%s",
                sign, websitemap.get("appid"), websitemap.get("mchid"), requestId, type, timeStanmp, nonceStr);
        return result;
    }

    /**
     * 微信预支付请求参数组包
     * ==============================================================================
     *
     * @param openid    公众号支付 用户关注的openid
     * @param tradeType 交易类型 JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
     * @return
     */
    @Override
    public SortedMap<String, String> createPackage(String description, String requestId, String productId,
                                                   BigDecimal amount, Integer userId, String reqIp,
                                                   String url, Map<String, String> websitemap,
                                                   String openid, String tradeType, HttpServletRequest request) {

        // 微信支付通知url
        String notifyUrl = (String) request.getAttribute("ctx") + url;
        // 随机字符串
        String noncestr = PackageUtil.getNonceStr();
        // 设置package订单参数
        SortedMap<String, String> packageParams = new TreeMap<String, String>();

        // ==================================================必须字段
        // appid
        packageParams.put("appid", websitemap.get("wxAppID"));
        // 商户号id
        packageParams.put("mch_id", websitemap.get("wxMchId"));
        // 商品描述
        packageParams.put("body", String.valueOf(description));
        // 通知地址
        packageParams.put("notify_url", notifyUrl);
        // 公众号支付 用户关注的openid
        packageParams.put("openid", openid);
        // 交易类型 JSAPI--公众号支付、NATIVE--原生扫码支付、APP--app支付
        packageParams.put("trade_type", tradeType);
        // 商户网站订单（也就是外部订单号，是通过客户网站传给支付宝，不可以重复）
        packageParams.put("out_trade_no", userId + DateUtil.toString(new Date(), "yyyyMMddHHmmssSSS"));
        String totalFee = amount.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).toString();
        // 订单总金额，单位为分，不能带小数点
        packageParams.put("total_fee", totalFee);
        // 订单生成的机器IP，指用户浏览器端IP
        packageParams.put("spbill_create_ip", reqIp);
        packageParams.put("product_id", productId);
        packageParams.put("nonce_str", noncestr);

        // ==================================================非必需字段
        // 附加数据 订单id 用户id
        packageParams.put("attach", requestId + "," + userId);
        Date date = new Date();
        SimpleDateFormat s = new SimpleDateFormat("yyyyMMddHHmmss");
        // 订单生成时间，格式为yyyyMMddHHmmss
        packageParams.put("time_start", s.format(date));

        // 订单3天后失效
        Calendar c = Calendar.getInstance();
        c.setTime(date);
        c.set(Calendar.DATE, c.get(Calendar.DATE) + 3);
        // 订单失效时间，格式为yyyyMMddHHmmss
        packageParams.put("time_expire", s.format(c.getTime()));

        return packageParams;
    }

    /**
     * 获取微信 密钥
     * ==============================================================================
     *
     * @return
     */
    @Override
    public Map<String, String> getWechatPayInfo() throws Exception {
        // 获得微信支付配置
        List<SysConfig> sysConfigs = sysConfigMapper.selectAll(new SysConfig(Constants.SysConfigType.SYS_CONFIG_TYPE_PAY_WECHAT));
        Map<String, Object> weixinMap = new HashMap<>();
        sysConfigs.forEach(t -> {
            weixinMap.put(t.getColumn(), t.getValue());
        });

        // 获得详细info
        if (weixinMap != null) {
            Map<String, String> websitemap = new HashMap<String, String>();
            // appId
            websitemap.put("appid", weixinMap.get("wxAppID").toString());
            // 支付密钥
            websitemap.put("payKey", weixinMap.get("wxPayKey").toString());
            // 商户id
            websitemap.put("mchid", weixinMap.get("wxMchId").toString());
            // 第三方用户唯一凭证密钥，即appsecret
            websitemap.put("appSecret", weixinMap.get("wxAppSecret").toString());
            return websitemap;
        }
        throw new Exception("Can not find WeChat-Configuration from DB!");
    }

    /**
     * 微信公众号支付请求(jspi支付): 发送预支付订单返回参数 total_fee 总金额减掉账户余额
     * ==============================================================================
     *
     * @param request
     * @param order
     * @param userProfile
     * @param totalFee    实际金额 总金额减掉账户金额
     * @return
     * @throws Exception
     */
    @Override
    public Model JSAPI(HttpServletRequest request, Order order, UserProfile userProfile, String totalFee) throws Exception {
        String productId = order.getNo() + "#" + order.getType();
        // 获得微信支付配置
        Map<String, String> websitemap = this.getWechatPayInfo();
        String payKey = websitemap.get("wxPayKey");
        String requestId = productId.split("#")[0];

        String description = String.format("%s: %s - %s", Constants.Order.COMPANY_NAME,
                dictionaryMapper.selectOne(new com.example.project.projectspringcloud.entity.sys.Dictionary("order-type", order.getType())).getName(),
                dictionaryMapper.selectOne(new Dictionary("video-type", order.getOrderDetail().getVideoType())).getName());
        // 请求参数包
        SortedMap<String, String> packageParams = this.createPackage(description, requestId, productId,
                new BigDecimal(totalFee), order.getUserId(), WebUtil.getIpAddr(request),
                "/pay/callback/2/notify", websitemap, userProfile.getValueTwo(),
                "JSAPI", request);

        // sign签名
        String sign = PackageUtil.createSign(packageParams, payKey);
        // 统一支付请求参数xml
        String xmlPackage = XMLParse.generateXmlPackage(packageParams, sign);
        // 预支付订单创建结果
        String resultXmlStr = HttpUtil.post("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlPackage);
        Map<String, String> resultMap = MessageUtil.parseStringXml(resultXmlStr);
        // 通信标识返回状态码
        String returnCode = resultMap.get("return_code");
        // 返回状态消息
        String returnMsg = resultMap.get("return_msg");
        if ("FAIL".equalsIgnoreCase(returnCode)) {
            logger.info("return_code-----fail:" + returnMsg);
        } else {
            // 业务结果
            String resultCode = resultMap.get("result_code");
            if ("FAIL".equalsIgnoreCase(resultCode)) {
                logger.info("result_code-----fail:err_code" + resultMap.get("err_code") + "/n" + "err_code_des" + resultMap.get("result_code"));
                logger.info("err_code:" + resultMap.get("err_code"));
                logger.info("err_code_des:" + resultMap.get("err_code_des"));
            } else {
                String prepayId = resultMap.get("prepay_id");
                // 微信内置 WeixinJSBridge 支付
                String timeMillis = System.currentTimeMillis() / 1000 + "";
                String nonceString = PackageUtil.getNonceStr();
                // 设置package订单参数
                SortedMap<String, String> paypackageParams = new TreeMap<String, String>();
                // appid
                paypackageParams.put("appId", websitemap.get("appid"));
                paypackageParams.put("timeStamp", timeMillis);
                paypackageParams.put("nonceStr", nonceString);
                paypackageParams.put("package", "prepay_id=" + prepayId);
                paypackageParams.put("signType", "MD5");
                // 签名
                String paysign = PackageUtil.createSign(paypackageParams, payKey);
                request.setAttribute("appId", websitemap.get("appid"));
                request.setAttribute("timeStamp", timeMillis);
                request.setAttribute("nonceStr", nonceString);
                request.setAttribute("info_package", "prepay_id=" + prepayId);
                request.setAttribute("paySign", paysign);
            }
        }
        return null;
    }

    @Override
    public String h5API(Order order, HttpServletRequest request) throws Exception {
        // 获得微信支付配置
        List<SysConfig> sysConfigs = sysConfigMapper.selectAll(new SysConfig(Constants.SysConfigType.SYS_CONFIG_TYPE_PAY_WECHAT));
        Map<String, String> weixinMap = new HashMap<>();
        sysConfigs.forEach(t -> {
            weixinMap.put(t.getColumn(), t.getValue());
        });

        String productId = order.getNo() + "#" + order.getType();
        // 获得微信支付配置
        String payKey = weixinMap.get("wxPayKey");
        String requestId = productId.split("#")[0];

        String description = String.format("%s: %s - %s", Constants.Order.COMPANY_NAME,
                dictionaryMapper.selectOne(new com.example.project.projectspringcloud.entity.sys.Dictionary("order-type", order.getType())).getName(),
                dictionaryMapper.selectOne(new Dictionary("video-type", order.getOrderDetail().getVideoType())).getName());
        // 请求参数包
        SortedMap<String, String> packageParams = this.createPackage(description, requestId, productId,
                order.getTotalPrice(), order.getUserId(), WebUtil.getIpAddr(request),
                "/pay/callback/2/payment", weixinMap, "", "MWEB", request);

        // sign签名
        String sign = PackageUtil.createSign(packageParams, payKey);
        // 统一支付请求参数xml
        String xmlPackage = XMLParse.generateXmlPackage(packageParams, sign);
        // 预支付订单创建结果
        String resultXmlStr = HttpUtil.doPostXml("https://api.mch.weixin.qq.com/pay/unifiedorder", xmlPackage);
        Map<String, String> resultMap = MessageUtil.parseStringXml(resultXmlStr);
        // 通信标识返回状态码
        String returnCode = resultMap.get("return_code");
        // 返回状态消息
        String returnMsg = resultMap.get("return_msg");
        if ("FAIL".equalsIgnoreCase(returnCode)) {
            logger.info("return_code-----fail:" + returnMsg);
        } else {
            // 业务结果
            String resultCode = resultMap.get("result_code");
            if ("FAIL".equalsIgnoreCase(resultCode)) {
                logger.info("result_code-----fail:err_code" + resultMap.get("err_code") + "/n" + "err_code_des" + resultMap.get("result_code"));
                logger.info("err_code:" + resultMap.get("err_code"));
                logger.info("err_code_des:" + resultMap.get("err_code_des"));
            } else {
                String prepayId = resultMap.get("prepay_id");
                // 微信内置 WeixinJSBridge 支付
                String timeMillis = System.currentTimeMillis() / 1000 + "";
                String nonceString = PackageUtil.getNonceStr();
                // 设置package订单参数
                SortedMap<String, String> paypackageParams = new TreeMap<String, String>();
                // appid
                paypackageParams.put("appId", weixinMap.get("wxAppID"));
                paypackageParams.put("timeStamp", timeMillis);
                paypackageParams.put("nonceStr", nonceString);
                paypackageParams.put("package", "prepay_id=" + prepayId);
                paypackageParams.put("signType", "MD5");
                // 签名
                String paysign = PackageUtil.createSign(paypackageParams, payKey);
                request.setAttribute("appId", weixinMap.get("wxAppID"));
                request.setAttribute("timeStamp", timeMillis);
                request.setAttribute("nonceStr", nonceString);
                request.setAttribute("info_package", "prepay_id=" + prepayId);
                request.setAttribute("paySign", paysign);
                return resultMap.get("mweb_url");
            }
        }
        return "";
    }

}


